package info.sean.util.ftp;



import com.jcraft.jsch.*;
import info.sean.logger.GmfLogger;
import info.sean.util.Args;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import java.io.*;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

public class SFTPClient {

    private static final Logger logger = GmfLogger.getLogger(SFTPClient.class);

    public static final String REMOTE_FILE_SEPARATOR = "/";    		//远程服务器文件目录分隔符

	private SFTPProperties properties;

    private ChannelSftp sftp = null;

    public SFTPClient(SFTPProperties properties) {
		Args.notNull(properties,"sftp properties");
        this.properties = properties;
    }

    public SFTPClient getChannel() throws JSchException {
        if(this.sftp == null || !this.sftp.isConnected()){
            synchronized (this){
                if(this.sftp == null || !this.sftp.isConnected()){
                    /**
                     * 链接 SFTP
                     */
                    JSch jsch = new JSch();
                    if (StringUtils.isNotEmpty(this.properties.getPrivateKeyPath())) {
                        if (StringUtils.isNotEmpty(this.properties.getPassPhrase())) {
                            jsch.addIdentity(this.properties.getPrivateKeyPath(),this.properties.getPassPhrase());
                        } else {
                            jsch.addIdentity(this.properties.getPrivateKeyPath());
                        }
                    }
                    Session sshSession = jsch.getSession(this.properties.getUserName(), this.properties.getHost(), this.properties.getPort());
                    if (StringUtils.isNotEmpty(this.properties.getPassword())) {
                        sshSession.setPassword(this.properties.getPassword());
                    }
                    Properties sshConfig = new Properties();
                    sshConfig.put("StrictHostKeyChecking", "no");
                    sshSession.setConfig(sshConfig);
                    // 设置超时时间为
                    sshSession.setTimeout(this.properties.getTimeout() * 1000);
                    sshSession.connect();
                    Channel channel = sshSession.openChannel("sftp");
                    channel.connect();
                    this.sftp = (ChannelSftp)channel;
                }
            }
        }
		Args.notNull(this.sftp,"ChannelSftp Object");
        return this;
    }

    /**
     * 切换目录
     */
    public SFTPClient cd(String path) throws SftpException {
        this.sftp.cd(path);
        return this;
    }

    /**
     * 创建目录
     */
    public SFTPClient mkdir(String pathStr) throws SftpException {
        String[] pathArray = pathStr.split(SFTPClient.REMOTE_FILE_SEPARATOR);
        for(String path : pathArray){
            try{
                if(StringUtils.isEmpty(path)){
                    this.cd("/");
                }else {
                    this.sftp.cd(path);
                }
            }catch (SftpException ex){
                if(ex.id == ChannelSftp.SSH_FX_NO_SUCH_FILE){
                    this.sftp.mkdir(path);
                    this.sftp.cd(path);
                }else{
                    throw ex;
                }
            }
        }
        return this ;
    }

    /**
     * 删除目录
     */
    public SFTPClient rmdir(String path, boolean recursive) throws SftpException {
        if (recursive) {
            Vector<ChannelSftp.LsEntry> dirList = this.sftp.ls(path);
            for (ChannelSftp.LsEntry entry : dirList) {
                if (!entry.getFilename().equals(".") && !entry.getFilename().equals("..")) {
                    path = path.endsWith(REMOTE_FILE_SEPARATOR) ? path : path + REMOTE_FILE_SEPARATOR;
                    if (entry.getAttrs().isDir()) {
                        rmdir(path + entry.getFilename(), true);
                    } else {
                        this.sftp.rm(path + entry.getFilename());
                    }
                }
            };
        }
        path = path.endsWith(REMOTE_FILE_SEPARATOR) ? path.substring(0, path.length() - 1) : path;
        this.sftp.rmdir(path);
        return this;
    }

    /**
     * 判断是否为文件夹
     * @param remoteDirectory
     * @return
     * @throws SftpException
     */
    public boolean isDirectory(String remoteDirectory) throws SftpException
    {
        return this.sftp.stat(remoteDirectory).isDir();
    }

    /**
     * 重命名
     */
    public SFTPClient rename(String oldName, String newName) throws SftpException {
        this.sftp.rename(oldName,newName);
        return this ;
    }

    /**
     * 文件是否存在
     */
    public boolean isFileExist(String path) throws SftpException {
        try {
            this.sftp.get(path);
            return true;
        } catch (SftpException e) {
            if(e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE){
                return false;
            }
            throw e;
        }
    }

    /**
     * 上传文件,同名覆盖
     */
    public SFTPClient upload(File file) throws IOException, SftpException {
        return this.upload(new FileInputStream(file),file.getName(),ChannelSftp.OVERWRITE);
    }

    /**
     * 上传文件,同名覆盖
     */
    public SFTPClient upload(InputStream inputStream,String fileName) throws IOException, SftpException {
        return this.upload(inputStream,fileName,ChannelSftp.OVERWRITE);
    }

    /**
     * 上传文件
     */
    public SFTPClient upload(File file,int model) throws IOException, SftpException {
        return this.upload(new FileInputStream(file),file.getName(),model);
    }

    /**
     * 上传文件
     */
    public SFTPClient upload(InputStream inputStream,String fileName,int model) throws IOException, SftpException {
        try {
            this.sftp.put(inputStream, fileName, model);
        }finally {
            inputStream.close();
        }
        return this;
    }

    public SFTPClient uploadFiles(String src, String dest, Boolean delOldFile) throws SftpException {
        if(delOldFile && isFileExist(dest)){
            rmdir(dest, true);
            mkdir(dest);
        }
        File file = new File(src);
        if (!file.exists()) {
            return this;
        }
        if (file.isDirectory()) {
            File[] subFiles = file.listFiles();
            dest = dest.endsWith(REMOTE_FILE_SEPARATOR) ? dest : dest + REMOTE_FILE_SEPARATOR;
            String mkdir = dest + file.getName();
            if (isFileExist(mkdir)) {
                this.sftp.cd(mkdir);
            } else {
                mkdir(mkdir);
            }
            if (subFiles == null || subFiles.length == 0) {
                return null;
            }
            for (File subFile : subFiles) {
                uploadFiles(subFile.getAbsolutePath(), mkdir, false);
            }
        } else {
            this.sftp.put(file.getAbsolutePath(), dest);
        }
        return this;
    }

    /**
     * 下载文件
     */
    public SFTPClient download(String fileName,OutputStream outputStream) throws SftpException {
        this.sftp.get(fileName,outputStream);
        return this ;
    }

    /**
     * 删除文件
     */
    public SFTPClient rm(String fileName) throws SftpException {
        try {
            this.sftp.rm(fileName);
        }catch (SftpException e) {
            if(e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE){
                return this;
            }
            throw e;
        }
        return this ;
    }

    /**
     * 列出文件
     * @param path
     * @return
     * @throws SftpException
     */
    public String[] ls(String path) throws SftpException {
        Vector<ChannelSftp.LsEntry> v = this.sftp.ls(path);
        if (v != null) {
            return v.stream().map(ChannelSftp.LsEntry::getFilename).toArray(String[]::new);
        }
        return null;
    }

    /**
     * 获取当前文件夹路径
     * @return
     * @throws SftpException
     */
    public String pwd() throws SftpException {
        return this.sftp.pwd();
    }

    /**
     * 关闭sftp连接
     */
    public void close() {
        if(this.sftp != null){
            if(this.sftp.isConnected()){
                this.sftp.disconnect();
            }
            try {
                if (this.sftp.getSession() != null) {
                    this.sftp.getSession().disconnect();
                }
            } catch (JSchException e) {
                logger.error("Close Sftp Connection Error:",e);
            }
        }
    }

    /**
     * 批量下载文件
     * @param remotePath ：远程下载目录(以路径符号结束,可以为相对路径eg:/assess/sftp/jiesuan_2/2014/)
     * @param localPath ：本地保存目录(以路径符号结束,D:\Duansha\sftp\)
     * @return
     */
    public Boolean downLoadFile(String remotePath, String localPath){
        try{
            localPath = localPath+File.separator+remotePath ;
            if(!new File(localPath).exists()){
				new File(localPath).mkdirs();
			}
            Vector v = sftp.ls(remotePath);
            if (v.size() > 0){
                System.out.println("本次总共待处理下载文件个数为fileSize=" + v.size());
                Iterator it = v.iterator();
                int  validFileCount = 0;
                while (it.hasNext()){
                    ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) it.next();
                    String filename = entry.getFilename();
                    SftpATTRS attrs = entry.getAttrs();
                    if (!entry.getFilename().equals(".") && !entry.getFilename().equals("..")) {
                        if (!attrs.isDir()) {
                            boolean flag = false;
							System.out.println("remotePath:" + remotePath+",localPath:" + localPath+",filename:" + filename);
                            flag = downloadFile(remotePath, filename, localPath, filename);
                            if (!flag) {
                                return false;
                            }
							validFileCount++;
                        } else {
                            String strremoteDirectoryPath = remotePath +  filename+"/";
                            downLoadFile( strremoteDirectoryPath,localPath);
                        }
                    }
                }
				System.out.println("本次处理的有效文件个数为fileSize=" + validFileCount);
            }
        }catch (SftpException e){
            e.printStackTrace();
        }
        return true;
    }

    public boolean downloadFile(String remotePath, String remoteFileName,String localPath, String localFileName){
        FileOutputStream fieloutput = null;
        try{
            File file = new File(localPath + localFileName);
            fieloutput = new FileOutputStream(file);
            sftp.get(remotePath + remoteFileName, fieloutput);
            return true;
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (SftpException e){
            e.printStackTrace();
        }finally{
            if (null != fieloutput){
                try{
                    fieloutput.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

}
